/**
 * Copyright (c) 2012 Himanshu Chauhan.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * @file cpu_features.h
 * @author Himanshu Chauhan (hschauhan@nulltrace.org)
 * @brief CPU specific features gathering information.
 */

#ifndef __CPU_FEATURES_H__
#define __CPU_FEATURES_H__

#include <libs/stringlib.h>
#include <vmm_stdio.h>
#include <vmm_types.h>
#include <arch_cache.h>
#include <cpu_msr.h>

/* Vendor-strings. */
#define CPUID_VENDOR_AMD	"AuthenticAMD"
#define CPUID_VENDOR_INTEL	"GenuineIntel"

#define CPUID_BASE_FEAT_BIT(_f, _r, _n) CPUID_FEAT_BASE_##_f##_##_r##_##_n##_BIT
#define CPUID_EXTD_FEAT_BIT(_f, _r, _n) CPUID_FEAT_EXTD_##_f##_##_r##_##_n##_BIT

#define __CPUID_FEAT_MASK(__bit) __bit##_MASK = (1UL << __bit)
#define DECLARE_CPUID_BASE_FEAT_REG_MASK(_f, _r, _m)		\
	__CPUID_FEAT_MASK(CPUID_FEAT_BASE_##_f##_##_r##_##_m##_BIT)
#define DECLARE_CPUID_EXTD_FEAT_REG_MASK(_f, _r, _m)		\
	__CPUID_FEAT_MASK(CPUID_FEAT_EXTENDED_##_f##_##_r##_##_m##_BIT)

/* To be used in side enum */
#define DECLARE_CPUID_BASE_FEAT_BIT_W_NUM(_f, _r, _b, _n)	\
	CPUID_FEAT_BASE_##_f##_##_r##_##_b##_BIT = (_n)
#define DECLARE_CPUID_BASE_FEAT_BIT_ENUM(_f, _r, _b)	\
	CPUID_FEAT_BASE_##_f##_##_r##_##_b##_BIT
#define DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(_f, _r, _b, _n)	\
	CPUID_FEAT_EXTENDED_##_f##_##_r##_##_b##_BIT = (_n)
#define DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(_f, _r, _b)	\
	CPUID_FEAT_EXTENDED_##_f##_##_r##_##_b##_BIT

enum {
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, FPU),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, VME),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, DE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PSE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, TSC),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, MSR),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PAE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, MCE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, CX8),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, APIC),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, RESVD10),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, SEP),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, MTRR),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PGE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, MCA),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, CMOV),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PAT),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PSE36),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PSN),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, CLF),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, NX), /* Reserved on Intel */
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, DTES),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, ACPI),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, MMX),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, FXSR),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, SSE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, SSE2),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, SS),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, HTT),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, TM1),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, IA64), /* Reserved on Intel */
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, EDX, PBE),
};

enum {
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SSE3),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, CM), /* Carryless multiplication (PCLMULQDQ) */
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, DTES64),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, MONITOR),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, DSCPL),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, VMX),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SMX),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, EIST),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, TM2),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SSSE3),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, CNXTID),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SDBG),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, FMA),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, CMPXCHG16B),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, XTPR),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, PDCM),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, RESVD16),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, PCID),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, DCA),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SSE4_1),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, SSE4_2),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, X2APIC),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, MOVBE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, POPCNT),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, TSCD), /* TSC Deadline support, Reserved on AMD */
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, AES),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, XSAVE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, OSXSAVE),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, AVX),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, F16C),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, RDRAND),
      DECLARE_CPUID_BASE_FEAT_BIT_ENUM(FEATURES, ECX, HYPERVISOR),
};

enum {
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, SSE3),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, CM),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, DTES64),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, MONITOR),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, DSCPL),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, VMX),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, SMX),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, EIST),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, TM2),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, SSSE3),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, CNXTID),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, FMA),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, CMPXCHG16B),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, PDCM),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, DCA),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, SSE4_1),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, SSE4_2),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, X2APIC),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, MOVBE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, POPCNT),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, TSCD),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, AES),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, XSAVE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, OSXSAVE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, AVX),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, F16C),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, ECX, RDRAND),
};

enum {
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, FPU),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, VME),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, DE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PSE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, TSC),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, MSR),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PAE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, MCE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, CX8),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, APIC),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, SEP),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, MTRR),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PGE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, MCA),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, CMOV),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PAT),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PSE36),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PSN),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, CLF),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, NX),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, DTES),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, ACPI),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, MMX),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, FXSR),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, SSE),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, SSE2),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, SS),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, HTT),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, TM1),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, IA64),
      DECLARE_CPUID_BASE_FEAT_REG_MASK(FEATURES, EDX, PBE),
};

enum {
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, LSAHF), /* LAHF/SAHF instruction supported */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, CMPLEGACY), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, SVM), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, EXTAPICSPACE), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, ALTMOVCR8), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, LZCNT),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, SSE4A), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, MISSALIGNSSE), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, PREFETCH),
      /* Every bit further is valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, OSVW),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, IBS),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, XOP),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, SKINIT),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, WDT),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD14),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, LWP),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, FMA4),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD17),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD18),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD19),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD20),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, TBM),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, TE), /* Topology extension */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, PCEC), /* Processor performance counter extensions support (AMD: E.4.2) */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, PCENB), /* NB performance counter extensions support. */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, RESVD25),
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, DBE), /* Data breakpoint extension */
      DECLARE_CPUID_EXTD_FEAT_BIT_ENUM(FEATURES, ECX, PERF_TSC),
      /* Futher reserved for AMD also */
};

enum {
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, LSAHF), /* LAHF/SAHF instruction supported */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, CMPLEGACY), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, SVM), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, EXTAPICSPACE), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, ALTMOVCR8), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, LZCNT),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, SSE4A), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, MISSALIGNSSE), /* valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, PREFETCH),
      /* Every bit further is valid only for AMD */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, OSVW),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, IBS),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, XOP),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, SKINIT),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, WDT),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD14),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, LWP),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, FMA4),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD17),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD18),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD19),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD20),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, TBM),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, TE), /* Topology extension */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, PCEC), /* Processor performance counter extensions support (AMD: E.4.2) */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, PCENB), /* NB performance counter extensions support. */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, RESVD25),
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, DBE), /* Data breakpoint extension */
      DECLARE_CPUID_EXTD_FEAT_REG_MASK(FEATURES, ECX, PERF_TSC),
      /* Futher reserved for AMD also */
};

enum {
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, SYS_CALL_RET, 11),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, NX, 20),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, MMX_EXT, 22),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, FFXSR, 25),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, PAGE1GB, 26),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, RDTSCP, 27),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, LM, 29), /* Long Mode */
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, 3DNOW_EXT, 30),
      DECLARE_CPUID_EXTD_FEAT_BIT_W_NUM(FEATURES, EDX, 3DNOW, 31),
};

enum cpuid_requests {
	CPUID_BASE_LFUNCSTD,
	CPUID_BASE_FEATURES,
	CPUID_BASE_TLB,
	CPUID_BASE_SERIAL,
	CPUID_BASE_CACHE_CONF,
	CPUID_BASE_MON,
	CPUID_BASE_PWR_MNG,
	CPUID_BASE_FEAT_FLAGS,

	CPUID_BASE_FUNC_LIMIT,

	CPUID_VM_CPUID_BASE=0x40000000UL,
	CPUID_VM_CPUID_MAX = 0x4FFFFFFFUL,

	CPUID_EXTENDED_LFUNCEXTD=0x80000000,
	CPUID_EXTENDED_FEATURES,
	CPUID_EXTENDED_BRANDSTRING,
	CPUID_EXTENDED_BRANDSTRINGMORE,
	CPUID_EXTENDED_BRANDSTRINGEND,
	AMD_CPUID_EXTENDED_L1_CACHE_TLB_IDENTIFIER,
	CPUID_EXTENDED_L2_CACHE_TLB_IDENTIFIER,

	CPUID_EXTENDED_RESVD_7,
	CPUID_EXTENDED_ADDR_BITS,
	AMD_CPUID_EXTENDED_ADDR_NR_PROC,
	CPUID_EXTENDED_RESVD_9,
	AMD_CPUID_EXTENDED_SVM_IDENTIFIER,

	CPUID_EXTENDED_FUNC_LIMIT
};

#define APIC_BASE(__msr)	(__msr >> 12)
#define APIC_ENABLED(__msr)	(__msr & (0x01UL << 11))

#define CPUID_BASE_FAMILY_SHIFT		8
#define CPUID_BASE_FAMILY_BITS		4
#define CPUID_BASE_FAMILY_MASK		((1<<CPUID_BASE_FAMILY_BITS)-1)
#define CPUID_EXTD_FAMILY_SHIFT		20
#define CPUID_EXTD_FAMILY_BITS		8
#define CPUID_EXTD_FAMILY_MASK		((1<<CPUID_EXTD_FAMILY_BITS)-1)

#define CPUID_BASE_MODEL_SHIFT		4
#define CPUID_BASE_MODEL_BITS		4
#define CPUID_BASE_MODEL_MASK		((1<<CPUID_BASE_MODEL_BITS)-1)
#define CPUID_EXTD_MODEL_SHIFT		16
#define CPUID_EXTD_MODEL_BITS		4
#define CPUID_EXTD_MODEL_MASK		((1<<CPUID_EXTD_MODEL_BITS)-1)

#define CPUID_STEPPING_SHIFT		0
#define CPUID_STEPPING_BITS		4
#define CPUID_STEPPING_MASK		((1<<CPUID_STEPPING_BITS)-1)

#define CPUID_L1_CACHE_SIZE_SHIFT	24
#define CPUID_L1_CACHE_SIZE_BITS	8
#define CPUID_L1_CACHE_SIZE_MASK	((1<<CPUID_L1_CACHE_SIZE_BITS)-1)
#define CPUID_L1_CACHE_LINE_SHIFT	0
#define CPUID_L1_CACHE_LINE_BITS	8
#define CPUID_L1_CACHE_LINE_MASK	((1<<CPUID_L1_CACHE_LINE_BITS)-1)

#define CPUID_L2_CACHE_SIZE_SHIFT	16
#define CPUID_L2_CACHE_SIZE_BITS	16
#define CPUID_L2_CACHE_SIZE_MASK	((1<<CPUID_L2_CACHE_SIZE_BITS)-1)
#define CPUID_L2_CACHE_LINE_SHIFT	0
#define CPUID_L2_CACHE_LINE_BITS	8
#define CPUID_L2_CACHE_LINE_MASK	((1<<CPUID_L2_CACHE_LINE_BITS)-1)

#define CPUID_L3_CACHE_SIZE_SHIFT	18
#define CPUID_L3_CACHE_SIZE_BITS	14
#define CPUID_L3_CACHE_SIZE_MASK	((1<<CPUID_L3_CACHE_SIZE_BITS)-1)
#define CPUID_L3_CACHE_LINE_SHIFT	0
#define CPUID_L3_CACHE_LINE_BITS	8
#define CPUID_L3_CACHE_LINE_MASK	((1<<CPUID_L3_CACHE_LINE_BITS)-1)

#define LVL_1_INST	1
#define LVL_1_DATA	2
#define LVL_2		3
#define LVL_3		4
#define LVL_TRACE	5

struct _cache_table {
	unsigned char descriptor;
	char cache_type;
	short size;
};

#define PROCESSOR_NAME_STRING_LEN	48
#define PROCESSOR_VENDOR_ID_LEN		12

enum x86_vendors {
	x86_VENDOR_UNKNOWN,
	x86_VENDOR_AMD,
	x86_VENDOR_INTEL,
	x86_NR_VENDORS,
};

struct cpuinfo_x86 {
	u8 vendor;
	u8 family;
	u8 model;
	u8 stepping;
	u8 vendor_string[PROCESSOR_VENDOR_ID_LEN];
	u8 name_string[PROCESSOR_NAME_STRING_LEN];
	u8 virt_bits;
	u8 phys_bits;
	u8 cpuid_level;
	u8 l1_dcache_size;
	u8 l1_dcache_line_size;
	u8 l1_icache_size;
	u8 l1_icache_line_size;
	u16 l2_cache_size;
	u16 l2_cache_line_size;
	u16 l3_cache_size;
	u8 hw_virt_available;
	u8 hw_nested_paging;
	u8 decode_assist;
	u32 hw_nr_asids;
	u32 tsc_khz; /* calibrated TSC frequency */
	u32 lapic_khz;
}__aligned(ARCH_CACHE_LINE_SIZE);

/*
 * Issue a single request to CPUID. Fits 'intel features', for instance
 * note that even if only "eax" and "edx" are of interest, other registers
 * will be modified by the operation, so we need to tell the compiler about it.
 */
static inline void cpuid(int code, u32 *a, u32 *b, u32 *c, u32* d)
{
	asm volatile("cpuid\n\t"
		:"=a"(*a), "=d"(*d), "=b"(*b), "=c"(*c)
		:"0"(code));
}

static inline u8 cpu_has_msr(void)
{
	u32 a, b, c, d;

	cpuid(CPUID_BASE_FEATURES,
	      &a, &b, &c, &d);

	return (d & CPUID_BASE_FEAT_BIT(FEATURES, EDX, MSR));
}

static inline u64 cpu_read_msr(u32 msr)
{
	u32 a, d;

	asm volatile ("rdmsr\n\t"
		      :"=a"(a),"=d"(d)
		      :"c"(msr)
		      :"rbx");

	return (u64)(((u64)d << 32)
		     | (a & 0xFFFFFFFFUL));
}

static inline void cpu_read_msr32(u32 msr, u32 *high, u32 *low)
{
	u32 a, d;

	asm volatile ("rdmsr\n\t"
		      :"=a"(a),"=d"(d)
		      :"c"(msr)
		      :"rbx");

	*high = d;
	*low = a;
}

static inline void cpu_write_msr(u32 msr, u64 value)
{
	u32 a, d;

	/* RDX contains the high order 32-bits */
	d = value >> 32;

	/* RAX contains low order */
	a = value & 0xFFFFFFFFUL;

	asm volatile ("wrmsr\n\t"
		      ::"a"(a),"d"(d),"c"(msr));
}

static inline void cpu_write_msr32(u32 msr, u32 high, u32 low)
{
	asm volatile ("wrmsr\n\t"
		      ::"a"(low),"d"(high),"c"(msr));
}

#define write_tsc(v1, v2) cpu_write_msr32(0x10, v2, v1)

extern struct cpuinfo_x86 cpu_info;
extern void indentify_cpu(void);

#endif /* __CPU_FEATURES_H */
